package fitness;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.Callable;

import utils.MyUtil;


public class ODESineFitness implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	protected static String outputName = "X"; //Which species is considered the output
	//protected static int defaultIndex = 0; //There is always at least one species
	
	protected static int skipTime = 300;

	protected static double penalty = 10000;
	
	protected static int numberofperiods = 10;
	
	public static double goal(int i, int firstPeak, double period, double height) {
		return (1 + Math.sin((i - firstPeak) * 2 * Math.PI
				/ period + Math.PI / 2))
				* height / 2;
	}

	public static OscillatorBasedResult evaluateFitness(double[][] data,ArrayList<String> symbols) {
		OscillatorBasedResult result = new OscillatorBasedResult();
		
		result.mse = Double.MAX_VALUE;
		result.count = 0;
		result.limitcycle = .1;
		result.average = 0.0;
//		if (symbols.size() == 1) {
//			result.sde = new Random().nextDouble() / penalty;
//			result.mse = 1000000;
//			return result;
//		}
		
		try {
			double period = 0;
			int output = symbols.indexOf(outputName);
			if (output == -1){
				//output = defaultIndex; //we use default if the target is not present
				result.average = -1;
				return result;
			}
				
			MyUtil.windowSizeForPeakSearching = 4;
			int firstPeak = MyUtil.getFirstPeak(data[output],
					skipTime);
			//System.out.println(firstPeak);
			if (firstPeak == -1 || firstPeak + 10 > data[output].length) 
			{
				int firstPeak2 = MyUtil.getFirstPeak(data[output], 0);
				//System.out.println("What");
				if (firstPeak2 == -1) 
				{
					result.sde = new Random().nextDouble() / penalty;
					result.mse = 1000000;
					return result;
				}
				else 
				{
					result.sde = (double) 1 / penalty;
					result.mse = 100000;
					return result;
				}
			}
			else 
			{
				int nextPeak = MyUtil.getFirstPeak(data[output], firstPeak
						+ 10);
				//System.out.println(nextPeak);
				if (nextPeak != -1) 
				{
					//System.out.println("Here !");
					period = nextPeak - firstPeak;
					//System.out.println(1.0/period);
					result.average = 1.0/((double)period); // dirty hack; We give the frequence now
					//System.out.println(period);
					result.limitcycle = 1
							/ (1 + Math.abs(data[output][firstPeak]
									- data[output][nextPeak])
									/ data[output][firstPeak])
							/ (1 + Math.abs(data[output][firstPeak]
									- data[output][nextPeak])
									/ data[output][firstPeak]); // changed YR
				}
			}

//			double mse = 0;
//			double average = 0;
//			double averageGoal = 0;
//
//			int calculationCount = 0;
//			for (int i = Math.max(firstPeak, skipTime); i < Math
//					.min(firstPeak + period
//							* (numberofperiods + 1),
//							data[output].length - 1); i++) {
//				average += data[output][i];
//				averageGoal += goal(i, firstPeak, period, data[output][firstPeak]);
//				calculationCount++;
//			}
//
//			average = average / calculationCount;
//			averageGoal = averageGoal / calculationCount;
//			double standdev = 0;
//			double standdevGoal = 0;
//			int count = 0;
//
//			for (int i = Math.max(firstPeak, skipTime); i < Math
//					.min(firstPeak + period
//							* (numberofperiods + 1),
//							data[output].length - 1); i++) {
//				double goal = goal(i, firstPeak, period, data[output][firstPeak]);
//				double error = goal - data[output][i];
//				mse += error * error;
//				standdev += (average - data[output][i])
//						* (average - data[output][i]);
//				standdevGoal += (averageGoal - goal) * (averageGoal - goal);
//				count++;
//			}
//			standdev /= count;
//			result.mse = mse / count;
//			result.sde = Math.min(5, standdev);
//			if (firstPeak == -1) {
//				result.sde /= penalty;
//			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
		return result;
	}

	public FitnessResult minFitness() {
		OscillatorBasedResult result = new OscillatorBasedResult();
		result.mse = Double.MAX_VALUE;
		result.count = 0;
		result.limitcycle = .1;
		return result;
	}

}
